﻿using System;
using System.Collections.Generic;
using System.Linq;

using log4net;

using VSTalk.Engine.Core.Context;
using VSTalk.Logger;
using VSTalk.Model;
using VSTalk.Tools;

namespace VSTalk.Engine.Core.Feed.MessageState
{
    public class MessageStateManager : IMessageSateManager, IListener, IDisposable
    {
        private static readonly ILog _log = LoggerFactory.Create(typeof(MessageStateManager));

        private AggregatedMessageCounter _aggregatedCounter;
        
        private readonly IDictionary<Interlocutor, UnreadMessageCounter> _counters = new Dictionary<Interlocutor, UnreadMessageCounter>();
        
        private ISubscription _interlocutorUnit;

        public static IMessageFeed Feed
        {
            get { return AppServices.Get<IMessageFeed>(); }
        }

        public IModelContext ModelContext
        {
            get { return AppServices.Get<IModelContext>(); }
        }

        public MessageStateManager()
        {
            _log.Info("Initializing MessageStateManager");
            StartListen();
            _log.Info("MessageStateManager initialized");
        }

        public void StartListen()
        {
            _aggregatedCounter = CreateAggregatedCounter();
            _interlocutorUnit = ModelContext
                    .Interlocutors
                    .HandleCollectionChanges(initialOrAdd: interlocutor =>
                    {
                        if (_counters.ContainsKey(interlocutor))
                        {
                            return;
                        }
                        var counter = new UnreadMessageCounter(interlocutor, this);
                        _counters.Add(interlocutor, counter);
                        Feed.Subscribe(counter);
                    }, removed: interlocutor =>
                    {
                        if (!_counters.ContainsKey(interlocutor)) return;
                        var counter = _counters[interlocutor];
                        foreach (var msg in counter.TrackingMessages.ToList())
                        {
                            MarkAsRead(msg);
                        }
                        Feed.Unsubscribe(counter);
                        _counters.Remove(interlocutor);
                    });
        }

        public void StopListen()
        {
            Feed.Unsubscribe(_aggregatedCounter);
            _interlocutorUnit.Unsubscribe();
        }

        public AggregatedMessageCounter AggregatedCounter
        {
            get { return _aggregatedCounter; }
        }

        private AggregatedMessageCounter CreateAggregatedCounter()
        {
            var counter = new AggregatedMessageCounter();
            Feed.Subscribe(counter);
            return counter;
        }

        public UnreadMessageCounter GetCounter(Interlocutor interlocutor)
        {
            if (_counters.ContainsKey(interlocutor))
            {
                return _counters[interlocutor];
            }
            _log.WarnFormat("There is no msg counter for interlocutor {0}", interlocutor.Id);
            return null;
        }

        public IEnumerable<Interlocutor> GetWaitingInterlocutors()
        {
            return from unreadMessageCounter in _counters 
                   where unreadMessageCounter.Value.HasMessages 
                   select unreadMessageCounter.Key;
        }

        public void MarkAsRead(IMessage message)
        {
            AggregatedCounter.MarkAsRead(message);
        }

        public void Dispose()
        {
            StopListen();
        }
    }
}